package com.ts.api.module.match.plan.internal.selection.strategy;

import com.gitee.apanlh.util.base.CollUtils;
import com.gitee.apanlh.util.base.IteratorUtils;
import com.gitee.apanlh.util.date.DateUtils;
import com.gitee.apanlh.util.reflection.ClassConvertUtils;
import com.gitee.apanlh.util.valid.ValidParam;
import com.ts.api.common.redis.MatchProductPlanRedisKey;
import com.ts.api.module.match.plan.internal.distribute.model.PlanDistributeField;
import com.ts.api.module.match.plan.internal.distribute.model.PlanDistributeResult;
import com.ts.api.module.match.plan.internal.selection.base.BasePriceSort;
import com.ts.api.module.product.entity.po.ProductPlanPO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;

/**
 *  价格优先
 *
 *  @author Pan
 */
@Component
public class ApiPriceSelection implements PlanSelection {

    @Autowired
    private BasePriceSort basePriceSort;

    @Override
    public PlanDistributeResult select(List<PlanDistributeResult> listSucMatchResult) {
        PlanDistributeField firstDistributeField = listSucMatchResult.getFirst().getPlanDistributeField();

        //  手机号md5模式
        if (ValidParam.isNotNull(firstDistributeField.getPhoneMd5Field())) {
            //  价格排序
            List<PlanDistributeResult> sortPhoneMd5Results = basePriceSort.sortByPhoneMd5(listSucMatchResult);
            //  搜索同价格被分发次数最少的机构计划
            PlanDistributeResult planDistributeResult = this.samePriceAllocateByPhoneMd5(sortPhoneMd5Results);
            return planDistributeResult;
        }

        //  手机号掩码模式
        if (ValidParam.isNotEmpty(firstDistributeField.getListPhoneMaskField())) {
            //  价格排序
            return basePriceSort.sortByPhoneMask(listSucMatchResult).getFirst();
        }
        return PlanDistributeResult.ofFail("筛选价格时出错，没有发现任何模式");
    }

    /**
     *  md5模式平均分配
     *
     *  @param  listSucMatchResult   所有成功匹配的结果
     *  @return ProductPlanPO
     */
    private PlanDistributeResult samePriceAllocateByPhoneMd5(List<PlanDistributeResult> listSucMatchResult) {
        if (listSucMatchResult.size() == 1) {
            return listSucMatchResult.getFirst();
        }

        //  最小被分发的计划
        TreeMap<Integer, ProductPlanPO> minAllocateMap = new TreeMap<>();

        //  同价位平均分配分发-避免出现分发不均匀
        Map<BigDecimal, List<PlanDistributeResult>> priceMap = CollUtils.groupByToLinked(listSucMatchResult, t -> t.getPlanDistributeField().getPhoneMd5Field().getPrice());
        Iterator<Map.Entry<BigDecimal, List<PlanDistributeResult>>> priceMapIterator = IteratorUtils.entrySet(priceMap);

        //  搜索最小分发机构次数
        while (priceMapIterator.hasNext()) {
            Map.Entry<BigDecimal, List<PlanDistributeResult>> next = priceMapIterator.next();
            List<PlanDistributeResult> listMatchResult = next.getValue();
            //  最高单价只返回一条则不搜索
            if (listMatchResult.size() <= 1) {
                minAllocateMap.put(1, IteratorUtils.getFirst(listMatchResult).getProductPlanPO());
                break;
            }

            //  搜索同价格未被分发过的机构计划
            for (int i = 0; i < listMatchResult.size(); i++) {
                ProductPlanPO productPlanPO = listMatchResult.get(i).getProductPlanPO();
                Object increment = MatchProductPlanRedisKey.PRICE_PRODUCT_PLAN.get(ClassConvertUtils.toStr(productPlanPO.getId()));
                //  此机构计划从未参与分发时将直接返回
                if (increment == null) {
                    minAllocateMap.put(1, productPlanPO);
                    break;
                }
                Integer incrementInt = ClassConvertUtils.toInt(increment);
                minAllocateMap.put(incrementInt, productPlanPO);
            }
            break;
        }

        //  如果最小分发中只有一个同价位，则直接返回首个，不记录平均分发次数
        if (minAllocateMap.size() == 1) {
            return listSucMatchResult.getFirst();
        }

        //  获取首个元素-记录redis操作-->自增计划-->并设置今日都明日0点自动清理
        ProductPlanPO minPlan = minAllocateMap.firstEntry().getValue();
        String key = ClassConvertUtils.toStr(minPlan.getId());

        MatchProductPlanRedisKey.PRICE_PRODUCT_PLAN.increment(key);

        long tomorrowMidnightMillis = DateUtils.getTomorrowMidnightMillis();
        MatchProductPlanRedisKey.PRICE_PRODUCT_PLAN.expire(key, tomorrowMidnightMillis, TimeUnit.MILLISECONDS);

        //  返回最终匹配计划
        return CollUtils.findOne(listSucMatchResult, t -> t.getProductPlanPO().getId().equals(minPlan.getId()));
    }
}
